package com.softa.common.sysconfig.shiro.session.dao;

import com.alicp.jetcache.Cache;
import com.alicp.jetcache.anno.CreateCache;
import com.softa.common.constant.CacheConstants;
import com.softa.common.utils.SerializableUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.apache.shiro.util.CollectionUtils;

import java.io.Serializable;
import java.util.*;

/**
 * @author hasee
 */
public class RedisSessionDAO extends AbstractSessionDAO {

	@CreateCache(name = CacheConstants.SHIRO_ACTIVESESSION_CACHE, expire = CacheConstants.TIME_OUT_3600)
	private Cache<Serializable, String> shiroSession;

	@CreateCache(name = CacheConstants.SHIRO_ACTIVESESSION_CACHE, expire = CacheConstants.TIME_OUT_MAX)
	private Cache<String, Set<Serializable>> keysSession;

	public RedisSessionDAO() {
	}

	@Override
	protected Serializable doCreate(Session session) {
		Serializable sessionId = generateSessionId(session);
		assignSessionId(session, sessionId);
		this.storeSession(sessionId, session);
		return sessionId;
	}

	@Override
	protected Session doReadSession(Serializable sessionId) {
		String sessions = shiroSession.get(sessionId);
		if(null == sessions){
			return null;
		}
		Session session = SerializableUtils.deserialize(sessions);
		return session;
	}

	@Override
	public void update(Session session) throws UnknownSessionException {
		if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid() || null == session.getId()) {
			return; //如果会话过期/停止 没必要再更新了
		}
		this.storeSession(session.getId(), session);
	}

	@Override
	public void delete(Session session) {
		Serializable id = session.getId();
		shiroSession.remove(id);
		removeKeys(id);
	}

	@Override
	public Collection<Session> getActiveSessions() {
		//TODO 会话进行验证，如果会话比较多，会影响性能；可以考虑如分页获取会话并进行验证
		String key = CacheConstants.SHIRO_CACHE_KEYS;
		Set<Serializable> keys = keysSession.get(key);
		Map<Serializable, String> mapValues = shiroSession.getAll(keys);
		Collection<String> sessions = mapValues.values();
		List<Session> values = new ArrayList<>();
		for(String s : sessions){
			values.add(SerializableUtils.deserialize(s));
		}
		return (Collection)(CollectionUtils.isEmpty(values)? Collections.emptySet():Collections.unmodifiableCollection(values));
	}

	protected Session storeSession(Serializable id, Session session) {
		if(id == null) {
			throw new NullPointerException("id argument cannot be null.");
		} else {
			shiroSession.put(id, SerializableUtils.serialize(session));
			putKeys(id);
			return session;
		}
	}

	public void putKeys(Serializable id){
		String key = CacheConstants.SHIRO_CACHE_KEYS;
		Set<Serializable> keys = keysSession.get(key);
		if(null == keys){
			keys = new HashSet<>();
		}
		keys.add(id);
		keysSession.put(key, keys);
	}

	public void removeKeys(Serializable id){
		String key = CacheConstants.SHIRO_CACHE_KEYS;
		Set<Serializable> keys = keysSession.get(key);
		if(null != keys){
			keys.remove(id);
			keysSession.put(key, keys);
		}
	}

}